<?php

/**
 * The file that defines the core WXTPP GH class
 *
 *
 * @link       https://git.oschina.net/wostech/wp-wxtpp
 * @since      1.0.0
 *
 * @package    WP_WXTPP
 * @subpackage WP_WXTPP/includes
 */

/**
 * The core WXTPP (WeiXin Third-Party Platform) GH class.
 *
 * @since      1.0.0
 * @package    WP_WXTPP
 * @subpackage WP_WXTPP/includes
 * @author     Yajun Hu <yjhu@126.com>
 */
class WXTPP_GH {
    const CPT_NAME = 'wxtpp_gh';
    const GH_OPTION_PREFIX = 'wxtpp-gh-';
    const GH_TRANSIENT_PREFIX = 'wxtpp-gh-';
    
    const RESPONSE_TEMPLATE_TEXT = 
        '<xml>
        <ToUserName><![CDATA[%s]]></ToUserName>
        <FromUserName><![CDATA[%s]]></FromUserName>
        <CreateTime>%s</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[%s]]></Content>
        </xml>';
    
    protected $appid;
    protected $access_token;
    protected $access_token_expires_in;
    protected $refresh_token;
    
    protected $authorized;
    protected $wxtpp;
    
    protected $post_id;
    protected $site_id;
    
    public function __construct( $appid ){ 
        if ( ! is_multisite() ) {
            $postid_gh = get_option( self::GH_OPTION_PREFIX . $appid );
            if ( FALSE === $postid_gh ) {
                $postid_gh = wp_insert_post( [
                    'post_type' =>self::CPT_NAME,
                    'post_title' => $appid,
                ] );
                update_option(  self::GH_OPTION_PREFIX . $appid, $postid_gh );                        
            }
            $this->post_id = $postid_gh;
            $this->appid = $appid;

            $this->access_token = get_post_meta( $this->post_id, 'access_token', TRUE );
            $this->access_token_expires_in = get_post_meta( $this->post_id, 'access_token_expires_in', TRUE );
            $this->refresh_token = get_post_meta( $this->post_id, 'refresh_token', TRUE );
            $this->authorized = get_post_meta( $this->post_id, 'authorized', TRUE );
        } else {
            $site_id = get_id_from_blogname( $appid );
            if ( empty( $site_id ) ) {
                $current_network = get_current_site();
                $site_id = wpmu_create_blog( $appid . '.' . $current_network->domain, $current_network->path, $appid, 1 );
            }
            
            $this->site_id = $site_id;
            $this->appid = $appid;
            
            $this->access_token = get_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'access_token' );
            $this->access_token_expires_in = get_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'access_token_expires_in' );
            $this->refresh_token = get_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'refresh_token' );
            $this->authorized = get_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'authorized' );            
        }
        
        require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-wp-wxtpp.php';
        $this->wxtpp = WXTPP::get_instance();
    }

    public function get_access_token( $force_loading = FALSE ) {
        if ( $force_loading || empty( $this->access_token ) || time() > $this->access_token_expires_in ) {
            if ( ! empty( $this->refresh_token ) ) {
                $response = $this->wxtpp->api_component_authorizer_token( $this->appid, $this->refresh_token );
                if ( false !== $response ) {
                    $this->set_access_token( $response[ 'authorizer_access_token' ], $response[ 'expires_in' ] + time() - 200 );
                    $this->set_refresh_token( $response[ 'authorizer_refresh_token' ] );
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
        return $this->access_token;
    }
    
    public function set_access_token( $token, $expires_in ) {
        if ( ! is_multisite() ) {
            update_post_meta( $this->post_id, 'access_token', $token );
            update_post_meta( $this->post_id, 'access_token_expires_in', $expires_in );
        } else {
            update_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'access_token', $token );
            update_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'access_token_expires_in', $expires_in );
        }
        $this->access_token = $token;
        $this->access_token_expires_in = $expires_in;
    }
    
    public function set_refresh_token( $refresh_token ) {
        if ( ! is_multisite() ) {
            update_post_meta( $this->post_id, 'refresh_token', $refresh_token );
        } else {
            update_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'refresh_token', $refresh_token );
        }
        $this->refresh_token = $refresh_token;
    }
    
    public function is_authorized() {
        return $this->authrized;
    }
    
    public function set_authorized( $authorized ) {
        if ( !is_multisite() ) {
            update_post_meta( $this->post_id, 'authorized', $authorized );
        } else {
            update_blog_option( $this->site_id, self::GH_OPTION_PREFIX . 'authorized', $authorized );
        }
        $this->authrized = $authorized;
    }
    
    public static function init() {
        if ( ! is_multisite() ) {
            register_post_type( self::CPT_NAME );
        }
    }
    
    /**
    * process WXTPP messages function.
    *
    * @since    1.0.0
    * @param WP_REST_Request $request Full data about the request.
    * @return WP_Error|array WXTPP response data or WP_Error on failure.
    */
    public function process( $request ) {
        $signature  = $request->get_param( 'signature' );
        $timestamp  = $request->get_param( 'timestamp' );
        $nonce      = $request->get_param( 'nonce' );

        if ( $this->wxtpp->check_signature($signature, $timestamp, $nonce) ) {
            $echostr = $request->get_param( 'echostr' );
            if ( ! empty( $echostr ) ) {
                die( $echostr );
            }
            
            $msg_arr = $this->wxtpp->decrypt_msg( $request );

            if ( FALSE !== $msg_arr ) {
                $this->update_user( $msg_arr[ 'ToUserName' ], $msg_arr[ 'FromUserName' ] );
                error_log( print_r( $msg_arr, TRUE ) );
                switch ( $msg_arr[ 'MsgType' ] ) {
                    case 'text':
                        die( $this->on_text( $msg_arr ) );
                }
            }

        }

//        die( $this->wxtpp->encrypt_msg( 'success' ) );
        die( 'success' );
    }
    
    private function on_text( $recieved_msg ) {
        $reply_msg = sprintf( 
            self::RESPONSE_TEMPLATE_TEXT, 
            $recieved_msg[ 'FromUserName' ], 
            $recieved_msg[ 'ToUserName' ],
            time(),
            $recieved_msg[ 'Content' ]
        );
//        error_log( print_r( $this->api_user_info( $recieved_msg[ 'FromUserName' ] ), TRUE ) );
        $encrypt_reply_msg = $this->wxtpp->encrypt_msg( $reply_msg );
//        error_log( print_r( $reply_msg, TRUE ) );
//        error_log( print_r( $encrypt_reply_msg, TRUE ) );
        return $encrypt_reply_msg;
    }
    
    public function get_appid() {
        return $this->appid;
    }
    
    public function get_siteid() {
        return $this->site_id;
    }
    
    public function can_call( $api_name ) {
        $errors = new WP_Error;
        $authorizer_info = $this->get_authorizer_info();
        if ( false === $authorizer_info ) {
            $errors->add( 
                'wxtpp_gh_api_error', 
                __( 'cannot get authorizer information' )
            );
        } else {
            switch ( $api_name ) {
                case 'api_user_info':
                    if ( $authorizer_info[ 'authorizer_info' ][ 'verify_type_info' ][ 'id' ] < 0 ) {
                        $errors->add( 
                            'wxtpp_gh_api_error', 
                            __( 'cannot call this api' ), 
                            [ 'api_user_info', $authorizer_info[ 'authorizer_info' ][ 'verify_type_info' ][ 'id' ] ] 
                        );
                    }
                    break;
            }
        }
        
        if ( 1 > count( $errors->get_error_codes() ) ) {
            return true;
        } else {
            return $errors;
        }
    }
    
    public function api_user_info( $openid ) {
        $can_call_this_api = $this->can_call( __FUNCTION__ );
        if ( is_wp_error( $can_call_this_api ) ) {
            return $can_call_this_api;
        }
        
        $access_token = $this->get_access_token();
        $response = json_decode( wp_remote_retrieve_body( wp_remote_get( "https://api.weixin.qq.com/cgi-bin/user/info?access_token={$access_token}&openid={$openid}&lang=zh_CN" ) ), TRUE );
        if ( isset( $response[ 'errcode' ] ) && 0 != $response[ 'errcode' ] ) {
            $this->error_log( __METHOD__, __FILE__, __LINE__, $response);
            return new WP_Error( 'wxtpp_gh_api_error', __('WXTPP GH API Failed.'), $response);
        } else {
            return $response;
        }        
    }
    
    private function error_log( $method, $file, $line, $response ) {
        error_log( "wxtpp api failed ({$method} at {$file}#{$line}): errcode = {$response[ 'errcode' ]}, errmsg = {$response[ 'errmsg' ]}" );
    }
    
    public function get_authorizer_info( $force_loading = false ) {
        $authorizer_info = get_site_transient( self::GH_TRANSIENT_PREFIX . $this->appid );
        if ( $force_loading || false === $authorizer_info ) {
            $response = $this->wxtpp->api_component_get_authorizer_info( $this->appid );
//            error_log( print_r( $response, true ) );            
            if ( false !== $response ) {
                $func_info = $response[ 'authorization_info' ][ 'func_info' ];
                $new_funcinfo = [];
                for ( $i = 0; $i < 15; $i++ ) $new_funcinfo[ $i ] = false;
                foreach ( $func_info as $item ) {
                    $id = $item[ 'funcscope_category' ][ 'id' ];
                    $new_funcinfo[ $id - 1 ] = true;
                }
                unset( $response[ 'authorization_info' ][ 'func_info' ] );
                $response[ 'authorization_info' ][ 'func_info' ] = $new_funcinfo;
//                error_log( print_r( $response, true ) ); 
                set_site_transient( self::GH_TRANSIENT_PREFIX . $this->appid, $response,  3600 * 1 );
                return $response;
            } else {
                return false;
            }
        }
        return $authorizer_info;
    }
    
    public static function get_service_type_info_str( $service_info_value ) {
        static $service_info_str = [
            '0' => '订阅号',
            '1' => '个人订阅号',
            '2' => '服务号',
        ];
        return $service_info_str[ "{$service_info_value}" ];
    } 
    
    /**
     * get_verify_type_info_str
     * 
     * @staticvar array $verify_info_str
     * @param integer $verify_info_value
     * @return string
     * 
     * 授权方认证类型:
     *      -1代表未认证，
     *      0代表微信认证，
     *      1代表新浪微博认证，
     *      2代表腾讯微博认证，
     *      3代表已资质认证通过但还未通过名称认证，
     *      4代表已资质认证通过、还未通过名称认证，但通过了新浪微博认证，
     *      5代表已资质认证通过、还未通过名称认证，但通过了腾讯微博认证
     */    
    public static function get_verify_type_info_str( $verify_info_value ) {
        static $verify_info_str = [
            '-1' => '未认证',
            '0' => '微信认证',
            '1' => '新浪微博认证',
            '2' => '腾讯微博认证',
            '3' => '微信认证（资质认证通过但还未通过名称认证）',
            '4' => '新浪微博认证（资质认证通过但还未通过名称认证）',
            '5' => '腾讯微博认证（资质认证通过但还未通过名称认证）',
        ];
//        error_log( print_r( $verify_info_str, true ) );
//        error_log( print_r( $verify_info_value, true ) );
//        error_log( print_r( $verify_info_str[ "{$verify_info_value}" ], true ) );
        return $verify_info_str[ "{$verify_info_value}" ];
    }
    
    private function update_user( $gh_id, $openid ) {
        $user = get_user_by( 'login', "{$openid}_in_{$gh_id}" );
        if ( false === $user ) {
            $user_id = wp_create_user( "{$openid}_in_{$gh_id}", "$gh_id", "{$openid}@{$gh_id}.com");
            if ( ! is_wp_error( $user_id ) ) {
                delete_user_option( $user_id, 'capabilities' );
                delete_user_option( $user_id, 'user_level' );
                add_user_to_blog( $this->get_siteid(), $user_id, 'subscriber' );
            }
        }
    }
}
